#pragma once

#include "vec3.h"
#include <assert.h>

template <class treal>
class mat3base
{
public:
    mat3base()
    {
        loadIdentity();
    }

    explicit mat3base(treal const * src)
    {
        memcpy(&mat[0][0], src, sizeof(mat));
    }

    explicit mat3base(
        treal const& v00, treal const& v10, treal const& v20,
        treal const& v01, treal const& v11, treal const& v21,
        treal const& v02, treal const& v12, treal const& v22
        )
    {
        mat[0][0] = v00;	mat[0][1] = v10;	mat[0][2] = v20;
        mat[1][0] = v01;	mat[1][1] = v11;	mat[1][2] = v21;
        mat[2][0] = v02;	mat[2][1] = v12;	mat[2][2] = v22;
    }

    inline treal getDeterminant()const
    {
        return
            mat[0][0] * (mat[1][1] * mat[2][2] - mat[2][1] * mat[1][2]) -
            mat[1][0] * (mat[0][1] * mat[2][2] - mat[2][1] * mat[0][2]) +
            mat[2][0] * (mat[0][1] * mat[1][2] - mat[1][1] * mat[0][2]);
    }

    inline void loadIdentity()
    {
        mat[0][1] = mat[0][2] = mat[1][0] = mat[1][2] = mat[2][0] = mat[2][1] = 0;
        mat[0][0] = mat[1][1] = mat[2][2] = 1;
    }

    inline basevec3<treal> getRow(unsigned i)const
    {
        assert(i < 3);
        return
            basevec3<treal>(
            mat[0][i],
            mat[1][i],
            mat[2][i]
        );
    }

    inline basevec3<treal> getColumn(unsigned i)const
    {
        assert(i < 3);
        return basevec3<treal>(mat[i]);
    }

    inline void setRow(unsigned i, const basevec3<treal>& v)
    {
        assert(i < 3);
        mat[0][i] = v.x;
        mat[1][i] = v.y;
        mat[2][i] = v.z;
    }

    inline void setColumn(unsigned i, const basevec3<treal> & v)
    {
        assert(i < 3);
        mat[i][0] = v.x;
        mat[i][1] = v.y;
        mat[i][2] = v.z;
    }


    treal mat[3][3];
};

typedef mat3base<double> mat3d;
typedef mat3base<float> mat3f;
